﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SixLabors.Fonts;
using SixLabors.ImageSharp.Drawing.Processing;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using StbImageSharp;
using TTEngine.Graphics;
namespace TTEngine.Impl.Desktop
{
    public class Loader : TTEngine.ILoader
    {
        bool CanChangePath = false;
        public Loader(bool _EditorMode)
        {
            this.CanChangePath = true;
            SetAssetPath(
                System.IO.Path.Combine(System.IO.Path.GetDirectoryName
                (Process.GetCurrentProcess().MainModule.FileName), "assets")
                );
            SetUserDataPath(
                 System.IO.Path.Combine(System.IO.Path.GetDirectoryName
                 (Process.GetCurrentProcess().MainModule.FileName), "userdata")
                );
            this.CanChangePath = _EditorMode;

        }
        string _assetpath;
        public string AssetPath
        {
            get
            {
                return _assetpath;

            }
        }

        string _userdatapath;
        public string UserDataPath
        {
            get
            {
                return _userdatapath;
            }
        }
        public void SetAssetPath(string path)
        {
            if (!CanChangePath)
                throw new Exception("不允许修改_assetpath");
            _assetpath = path;
            if (!System.IO.Directory.Exists(_assetpath))
            {
                System.IO.Directory.CreateDirectory(_assetpath);
            }
        }
        public void SetUserDataPath(string path)
        {
            if (!CanChangePath)
                throw new Exception("不允许修改_userdatapath");
            _userdatapath = path;
            if (!System.IO.Directory.Exists(_userdatapath))
            {
                System.IO.Directory.CreateDirectory(_userdatapath);
            }
        }
        public async Task<byte[]> LoadAssetBinAsync(string path)
        {
            return await System.IO.File.ReadAllBytesAsync(System.IO.Path.Combine(AssetPath, path));
        }

        public async Task<string> LoadAssetTextAsync(string path)
        {
            return await System.IO.File.ReadAllTextAsync((System.IO.Path.Combine(AssetPath, path)));
        }
   
        public IImage LoadImage(byte[] _data)
        {


            var img = ImageResult.FromMemory(_data, ColorComponents.RedGreenBlueAlpha);
            var imgdata = new ImageData(img.Width, img.Height, ImageFormat.RGBA);
            var targetdata = imgdata.GetData();
            for (var i = 0; i < img.Data.Length; i++)
            {
                imgdata.GetData()[i] = img.Data[i];
            }

            return imgdata;
        }


        public IAnimatedImage LoadAnimatedImage(byte[] _data)
        {

            using var ms = new System.IO.MemoryStream(_data);
            var img = new AnimatedGifEnumerable(ms, ColorComponents.RedGreenBlueAlpha);

            var imgani = new AnimatedImage();
            foreach (var frame in img)
            {
                var imgdata = new ImageFrameData(frame.Width, frame.Height,
                    ImageFormat.RGBA, frame.DelayInMs);
                imgdata.SetData(frame.Data);
                imgani.AddFrame(imgdata);
            }
            return imgani;
        }

        public ISystemTtfFontFamily LoadTTF(byte[] _data)
        {
            FontCollection fontcoll = new FontCollection();

            using var ss = new System.IO.MemoryStream(_data);
            var fontfamily = fontcoll.Add(ss);

            return new FontTTFFamily(fontfamily);
        }



    }


    public class FontTTF : ISystemTtfFont
    {
        Font font;
        public FontTTF(Font font)
        {
            this.font = font;

        }
        public string Name => this.font.Name;
        public int Size => (int)this.font.Size;
        public SpriteData GetCharImage(char c, int border)
        {
            var charwidth = GetCharWidth(c);
            if (charwidth == 0)
                return null;
            var fontsize = Size;
            SpriteData img = new SpriteData(
                fontsize + border * 2, fontsize + border * 2, ImageFormat.Gray,
                border, border);

            var image = new SixLabors.ImageSharp.Image<L8>(img.Width, img.Height);

            var text = c.ToString();


            image.Mutate(ctx =>
            {
                ctx.Clear(SixLabors.ImageSharp.Color.Black);
               
                ctx.DrawText(text, font, SixLabors.ImageSharp.Color.White, new SixLabors.ImageSharp.PointF(border, border));
            });

            var targetdata = img.GetData();
            image.ProcessPixelRows(accessor =>
            {
                for (var y = 0; y < accessor.Height; y++)
                {
                    var row = accessor.GetRowSpan(y);
                    for (var x = 0; x < row.Length; x++)
                    {
                        targetdata[y * img.Width + x] = row[x].PackedValue;
                    }
                }
            });
            return img;

        }

        public int GetCharWidth(char c)
        {

            if (!font.FontMetrics.TryGetGlyphMetrics(new SixLabors.Fonts.Unicode.CodePoint(c),
                TextAttributes.None, TextDecorations.None, LayoutMode.HorizontalTopBottom, ColorFontSupport.None,
                out var gmetrics))
                return 0;
            float wid = (float)gmetrics[0].Width;
            float adv = (float)gmetrics[0].AdvanceWidth;
            float l = (float)gmetrics[0].LeftSideBearing;
            float r = (float)gmetrics[0].RightSideBearing;
            float wid2 = l + adv - r;
            float w = System.MathF.Max(wid, wid2) / gmetrics[0].UnitsPerEm * Size;
            return (int)w;
            //return gmetrics[0].AdvanceWidth;
        }
    }
    public class FontTTFFamily : ISystemTtfFontFamily
    {
        FontFamily fontfamily;

        public FontTTFFamily(FontFamily ff)
        {
            this.fontfamily = ff;
        }

        public string Name => this.fontfamily.Name;
        Dictionary<int, FontTTF> fonts = new Dictionary<int, FontTTF>();
        public ISystemTtfFont GetFont(int fontsize)
        {
            if (!fonts.TryGetValue(fontsize, out var font))
            {
                font = new FontTTF(fontfamily.CreateFont(fontsize));
                fonts[fontsize] = font;
            }
            return font;
        }


    }

}
